Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add token list/delete endpoints #42402

Merged
merged 1 commit into from
Jun 7, 2024
Merged

Add token list/delete endpoints #42402

merged 1 commit into from
Jun 7, 2024

Conversation

avatus
Copy link
Contributor

@avatus avatus commented Jun 4, 2024

This PR implements list/delete for tokens in the api the same way as
tctl interacts with them. This does not include pagination yet but is
currently being used while the feature is in development. Pagination can
come in the future in the form of a sort cache, but the endpoints won't
change

@avatus avatus added the no-changelog Indicates that a PR does not require a changelog entry label Jun 4, 2024
@avatus avatus force-pushed the avatus/api_tokens branch from 947dc38 to d44bfb3 Compare June 4, 2024 19:29
Comment on lines 27 to 28
// Id is the name of the token
Id string `json:"id"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Id is the name of the token
Id string `json:"id"`
// ID is the name of the token
ID string `json:"id"`

// SafeName returns the name of the token, sanitized appropriately for
// join methods where the name is secret.
SafeName string `json:"safeName"`
// Expiry is the time that the token resource expires
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd mention what value to expect for tokens which do not expire.


func MakeJoinToken(token types.ProvisionToken) JoinToken {
labels := token.GetSuggestedLabels()
suggestedLabels := make([]Label, len(labels))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
suggestedLabels := make([]Label, len(labels))
suggestedLabels := make([]Label, 0, len(labels))

Otherwise suggestedLabels is going to be twice as long as you expect.

}, nil
}

func (h *Handler) deleteToken(_ http.ResponseWriter, r *http.Request, params httprouter.Params, ctx *SessionContext) (interface{}, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed you _'d the ResponseWriter here, but not in getTokens. I would be consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to put the w back in, as that is consistent with the rest of the api. I used the _ because my linter was throwing a warning but now it isn't? weird. oh well!

expiry time.Time
}

func Test_getTokens(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func Test_getTokens(t *testing.T) {
func TestGetTokens(t *testing.T) {

ctx := context.Background()
expiry := time.Now().UTC().Add(30 * time.Minute)

staticUiToken := ui.JoinToken{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
staticUiToken := ui.JoinToken{
staticUIToken := ui.JoinToken{

Comment on lines 132 to 135
err = clt.DeleteToken(r.Context(), token)
if err != nil {
return nil, trace.Wrap(err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💅

Suggested change
err = clt.DeleteToken(r.Context(), token)
if err != nil {
return nil, trace.Wrap(err)
}
if err := clt.DeleteToken(r.Context(), token); err != nil {
return nil, trace.Wrap(err)
}

SafeName: token.GetSafeName(),
Expiry: token.Expiry(),
Roles: token.GetRoles(),
IsStatic: token.Expiry().Equal(time.Unix(0, 0).UTC()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this one.
A static token is a token that is configured directly into the teleport.yaml in the auth service. Those exist inside the type types.StaticTokensV2

Which is different from a token with no expiration, which you can create dynamically.
So while static tokens don't have expiration, not all Expiry == 0 are static tokens.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Static tokens are set from the config with zero Unix time (1970-01-01), but if I manually configure a token with no ttl, it's actually set to zero time (0001-01-01) so it's not an ideal distinction but it does work.

Ideally I wanted to use the Origin but it seems the origin for all tokens (in my testing) just return empty string.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL, thank you.
Can we convert this check into a token's method? I think this distinction should be really close to the token's definition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean something like making JoinToken.IsStaic() ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

@@ -547,6 +553,20 @@ func ProvisionTokensFromV1(in []ProvisionTokenV1) []ProvisionToken {
return out
}

// ProvisionTokensFromStatic converts static tokens to resource list
func ProvisionTokensFromStatic(in []ProvisionTokenV1) []ProvisionToken {
Copy link
Contributor Author

@avatus avatus Jun 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added this method to replace ProvisionTokensFromV1 and all instances that I could find using the old method had to deal with static tokens. I could have just thrown in this SetOrigin there but I was worried it would be used for something that isnt a static token. If that isn't the case, I'll remove this new ProvisionTokensFromStatic method and throw it into the old one. Curious your thoughts

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's fine, but it would be better if we changed it at its creation instead of when converting to something else.
So, either on their CheckAndSetDefaults or when parsing the token from the file.

if err := st.CheckAndSetDefaults(); err != nil {

tokens, err := st.Parse()

func (p *ProvisionTokenV2) IsStatic() bool {
return p.Origin() == OriginConfigFile
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I added the IsStatic method but I am not relying on the time anymore (it seemed fickle to me from the start). When we fetch static tokens, we create a new ProvisionToken from the available static tokens. I've just set their origin to OriginConfiguration instead of being nil and now IsStatic just checks if Origin() == OriginConfiguration. It seems to be more correct and resilient this way. Let me know what you think

AllowRules []string `json:"allowRules,omitempty"`
}

func MakeJoinToken(token types.ProvisionToken) JoinToken {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:
what would you say about ConvertToJoinToken ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all the other "make ui struct" methods for the other UI resources follow the MakeX pattern so I'll keep it to stay consistent.

}
}

func MakeJoinTokens(tokens []types.ProvisionToken) (joinTokens []JoinToken) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func MakeJoinTokens(tokens []types.ProvisionToken) (joinTokens []JoinToken) {
func ConvertToJoinTokens(tokens []types.ProvisionToken) (joinTokens []JoinToken) {

"github.com/gravitational/teleport/api/types"
)

type JoinToken struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: missing docs.

Comment on lines 51 to 56
for labelKey, labelValues := range labels {
suggestedLabels = append(suggestedLabels, Label{
Name: labelKey,
Value: strings.Join(labelValues, " "),
})
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit breaking way to return labels where labels are joined by space. What if a label contains a space like m = {"zone: []string{"district 1", "district 2"}} ?

Can we reuse ui.makeLabels

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've decided to remove the suggested labels all together from the UI table, so i'll just remove this code. We will be sending the full json value of the token as well for a "Details" page where this information will be available, but we don't need it in the UI struct individually

h.POST("/webapi/token", h.WithAuth(h.createTokenHandle))
h.GET("/webapi/tokens", h.WithAuth(h.getTokens))
h.DELETE("/webapi/tokens/:id", h.WithAuth(h.deleteToken))
Copy link
Contributor

@smallinsky smallinsky Jun 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we hide the :id in the DELETE request body instead of exposing it in URL ?

Let say that a user has permission to list tokens but it don't have permission to delete tokens. And a customer infrastructure tools like:

LB -> Ingress -> Teleport Proxy

Please note that :id == token_secret in case of static tokens.

When a client calls /webapi/tokens/token_secret with missing permission to delete secret the Ingress can log the request HTTP Path and expose token_secret in Ingress logs.

Copy link
Contributor Author

@avatus avatus Jun 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea, i'll make the change! typically DELETE requests don't have a body and I don't think our client supports it but I'll see how it works

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided to send via a custom header as some servers/load balancers might just ignore any request body sent with a DELETE

@avatus avatus force-pushed the avatus/api_tokens branch from 56dda05 to 24d7fc1 Compare June 6, 2024 22:06
@public-teleport-github-review-bot public-teleport-github-review-bot bot removed the request for review from EdwardDowling June 7, 2024 07:28
@@ -53,6 +53,7 @@ import (

const (
stableCloudChannelRepo = "stable/cloud"
HeaderTokenName = "X-TOKEN-NAME"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be something like X-Teleport-TokenName?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes it should

@avatus avatus force-pushed the avatus/api_tokens branch from 88363ca to edb9a20 Compare June 7, 2024 17:24
This PR implements list/delete for tokens in the api the same way as
tctl interacts with them. This does not include pagination yet but is
currently being used while the feature is in development. Pagination can
come in the future in the form of a sort cache, but the endpoints won't
change
@avatus avatus force-pushed the avatus/api_tokens branch from 4b5f250 to ffc7071 Compare June 7, 2024 22:40
@avatus avatus enabled auto-merge June 7, 2024 22:45
@avatus avatus added this pull request to the merge queue Jun 7, 2024
Merged via the queue into master with commit 1a271a4 Jun 7, 2024
37 checks passed
@avatus avatus deleted the avatus/api_tokens branch June 7, 2024 23:17
avatus added a commit that referenced this pull request Jul 26, 2024
This PR implements list/delete for tokens in the api the same way as
tctl interacts with them. This does not include pagination yet but is
currently being used while the feature is in development. Pagination can
come in the future in the form of a sort cache, but the endpoints won't
change
@avatus avatus mentioned this pull request Jul 26, 2024
avatus added a commit that referenced this pull request Aug 13, 2024
This PR implements list/delete for tokens in the api the same way as
tctl interacts with them. This does not include pagination yet but is
currently being used while the feature is in development. Pagination can
come in the future in the form of a sort cache, but the endpoints won't
change
github-merge-queue bot pushed a commit that referenced this pull request Aug 26, 2024
* Add token list/delete endpoints (#42402)

This PR implements list/delete for tokens in the api the same way as
tctl interacts with them. This does not include pagination yet but is
currently being used while the feature is in development. Pagination can
come in the future in the form of a sort cache, but the endpoints won't
change

* Add token ACL/routes

* Add initial join tokens UI

* Add Allow and GCP fields to ui join token

* Fix missing styled components

* Fix empty join token state (#45413)

Empty join token responses would show an error instead of an empty list
message

* Update button case
github-merge-queue bot pushed a commit that referenced this pull request Aug 26, 2024
* Add token list/delete endpoints (#42402)

This PR implements list/delete for tokens in the api the same way as
tctl interacts with them. This does not include pagination yet but is
currently being used while the feature is in development. Pagination can
come in the future in the form of a sort cache, but the endpoints won't
change

* Add token ACL/routes

* Add initial join tokens UI

* Add Allow and GCP fields to ui join token

* Fix missing styled components

* Fix empty join token state (#45413)

Empty join token responses would show an error instead of an empty list
message

* Update button case
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
no-changelog Indicates that a PR does not require a changelog entry size/md
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants